package com.stellar.wechat.core.message;

import com.stellar.wechat.config.WxMiniProgramConfig;
import com.stellar.wechat.core.common.AccessTokenUtil;
import com.stellar.wechat.core.common.WxHttpClient;
import com.stellar.wechat.core.common.exception.WxApiException;

import com.stellar.wechat.core.entity.WxTemplateField;
import com.stellar.wechat.core.entity.WxTemplateFieldType;
import com.stellar.wechat.core.enums.WxErrorCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.URI;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;


@Slf4j
@Service
public class WxMessageServiceImpl implements WxMessageService {

    private final WxMiniProgramConfig config;

    private final AccessTokenUtil accessTokenUtil;

    private final WxHttpClient wxHttpClient;

    private static final Set<String> VALID_TEMPLATE_IDS = new HashSet<>();

    private static final String SUBSCRIBE_MESSAGE_URL = "/cgi-bin/message/subscribe/send";

    public WxMessageServiceImpl(WxMiniProgramConfig config,
                                AccessTokenUtil accessTokenUtil,
                                WxHttpClient wxHttpClient) {
        this.config = config;
        this.accessTokenUtil = accessTokenUtil;
        this.wxHttpClient = wxHttpClient;
    }

    @PostConstruct
    public void init() {
        // 初始化时加载有效模板ID（可替换为数据库查询）
        VALID_TEMPLATE_IDS.addAll(Arrays.asList("TEMPLATE_ID_1", "TEMPLATE_ID_2"));
    }

    @Override
    public void sendSubscribeMessage(String openid, String templateId, Map<String, Object> data)
            throws IOException, WxApiException {
        // 参数校验
        validateParams(openid, templateId);

        // 获取有效的AccessToken
        String accessToken = getValidAccessToken();

        // 格式化模板数据
        Map<String, Object> formattedData = buildFormattedData(data);

        try {
            executeRequest(openid, templateId, accessToken, formattedData);
            log.info("消息发送成功 - OpenID: {}, Template: {}", openid, templateId);
        } catch (WxApiException ex) {
            handleApiException(ex, openid, templateId, formattedData);
        }
    }

    private void validateParams(String openid, String templateId) {
        if (StringUtils.isBlank(openid)) {
            throw new IllegalArgumentException("OpenID不能为空");
        }
        if (!VALID_TEMPLATE_IDS.contains(templateId)) {
            throw new IllegalArgumentException("无效模板ID: " + templateId);
        }
    }

    private String getValidAccessToken() throws WxApiException {
        try {
            return accessTokenUtil.getAccessToken(false);
        } catch (WxApiException ex) {
            if (ex.getErrcode() == WxErrorCode.INVALID_ACCESS_TOKEN.getCode()) {
                return accessTokenUtil.getAccessToken(true); // 强制刷新
            }
            throw ex;
        }
    }

    private Map<String, Object> buildFormattedData(Map<String, Object> rawData) {
        Map<String, Object> formattedData = new LinkedHashMap<>();
        rawData.forEach((key, value) -> {
            if (value instanceof WxTemplateField) {
                WxTemplateField field = (WxTemplateField) value;
                String fieldKey = key + "." + field.getType().getSuffix();
                formattedData.put(fieldKey, formatFieldValue(field));
            } else {
                log.warn("未指定类型的模板字段: {}", key);
                formattedData.put(key + ".DATA", Collections.singletonMap("value", value.toString()));
            }
        });
        return formattedData;
    }

    private Map<String, String> formatFieldValue(WxTemplateField field) {
        Map<String, String> valueMap = new HashMap<>();
        try {
            String formattedValue = formatValue(field.getValue(), field.getType());
            valueMap.put("value", formattedValue);
        } catch (Exception ex) {
            throw new WxApiException(WxErrorCode.INVALID_TEMPLATE_DATA);
        }
        return valueMap;
    }

    private String formatValue(Object value, WxTemplateFieldType type) {
        switch (type) {
            case TIME:
                LocalDateTime time = (LocalDateTime) value;
                return time.format(DateTimeFormatter.ofPattern(type.getFormatRule().toString()));
            case NUMBER:
                if (!(value instanceof Number)) {
                    throw new IllegalArgumentException("数字类型必须为数值");
                }
                return value.toString();
            case THING:
                String strValue = value.toString();
                if (strValue.length() > Integer.valueOf(type.getFormatRule())) {
                    throw new IllegalArgumentException("文本长度超过限制: " + type.getFormatRule());
                }
                return strValue;
            default:
                throw new UnsupportedOperationException("未支持的类型: " + type);
        }
    }

    private void executeRequest(String openid, String templateId, String accessToken,
                                Map<String, Object> data) throws IOException, WxApiException {
        URI uri = UriComponentsBuilder.fromHttpUrl(config.getApiBaseUrl())
                .path(SUBSCRIBE_MESSAGE_URL)
                .queryParam("access_token", accessToken)
                .build().toUri();

        Map<String, Object> requestBody = new LinkedHashMap<>();
        requestBody.put("touser", openid);
        requestBody.put("template_id", templateId);
        requestBody.put("data", data);
        requestBody.put("miniprogram_state", "formal");
        requestBody.put("lang", "zh_CN");

        wxHttpClient.post(uri, requestBody, Void.class);
    }

    private void handleApiException(WxApiException ex, String openid, String templateId,
                                    Map<String, Object> data) throws IOException, WxApiException {
        log.error("消息发送失败 - OpenID: {}, 错误码: {}, 错误信息: {}",
                openid, ex.getErrcode(), ex.getMessage());

        switch (ex.getErrcode()) {
            case 40001: // 无效Token
                String newToken = accessTokenUtil.getAccessToken(true);
                executeRequest(openid, templateId, newToken, data);
                break;
            case 43101: // 用户拒收
                log.warn("用户已拒收消息 - OpenID: {}", openid);
                break;
            default:
                throw ex;
        }
    }
}